/*
 *  ifxmetadata.cpp - Informix dbExpress metadata implementation
 *
 *    Copyright (c) 2001-2006, Luxena Software Company. All rights reserved.
 *
 *  Purpose:
 *
 */

#include "ifxmetadata.hpp"

#include <new>
#include <memory>
#include <string.h>
#include <sqliapi.h>
#include "dbxcommon.hpp"

const int INFORMIX_IDENTIFIER_LENGTH = 128;

/////////////////////////////////////////////////////////////////////////////

const pCHAR TABLES_SQL =
	"select \
		tabid recno, \
		' ' catalog_name, \
		owner schema_name, \
		tabname table_name, \
		-1 table_type, \
		tabtype \
	from \
		informix.systables \
	where \
	    ((tabid >= 100 and tabtype in (?, ?, ?, ?)) or (tabid < 100 and ? = 1 and tabtype <> '')) \
		and tabname like ? \
	order by 4";

const int TABLES_RECNO = 1;
const int TABLES_CATALOG_NAME = 2;
const int TABLES_SCHEMA_NAME = 3;
const int TABLES_TABLE_NAME = 4;
const int TABLES_TABLE_TYPE = 5;
const int TABLES_TABTYPE = 6;


/////////////////////////////////////////////////////////////////////////////

const pCHAR PROCEDURES_SQL =
	"select \
		procid recno, \
		' ' catalog_name, \
		owner schema_name, \
		procname proc_name, \
		1 proc_type, \
		numargs in_params, \
		0 out_params \
	from \
		informix.sysprocedures \
	where \
		procname like ? \
	order by 4";


/////////////////////////////////////////////////////////////////////////////

const pCHAR COLUMNS_SQLS[] =
{
	// Informix SE and 7 family compatible SQL
	"select \
		sc.rowid recno, \
		' ' catalog_name, \
		st.owner schema_name, \
		st.tabname table_name, \
		sc.colname column_name, \
		sc.colno column_position, \
		0 column_type, \
		0 column_datatype, \
		' ' column_typename, \
		0 column_subtype, \
		0 column_precision, \
		0 column_scale, \
		sc.collength column_length, \
		0 column_nullable, \
		sc.coltype, \
		0 extended_id, \
		' ' typename \
	from \
		informix.systables st, \
		informix.syscolumns sc \
	where \
		st.tabname like ? \
		and sc.tabid = st.tabid \
		and sc.colname like ? \
	order by 3, 4, 6",

	// Informix 9 family compatible SQL
	"select \
		sc.rowid recno, \
		' ' catalog_name, \
		st.owner schema_name, \
		st.tabname table_name, \
		sc.colname column_name, \
		sc.colno column_position, \
		0 column_type, \
		0 column_datatype, \
		' ' column_typename, \
		0 column_subtype, \
		0 column_precision, \
		0 column_scale, \
		sc.collength column_length, \
		0 column_nullable, \
		sc.coltype, \
		sc.extended_id, \
		xt.name typename \
	from \
		informix.systables st, \
		informix.syscolumns sc, \
		outer informix.sysxtdtypes xt \
	where \
		st.tabname like ? \
		and sc.tabid = st.tabid \
		and xt.extended_id = sc.extended_id \
		and sc.colname like ? \
	order by 3, 4, 6"
};

const int COLUMNS_RECNO = 1;
const int COLUMNS_CATALOG_NAME = 2;
const int COLUMNS_SCHEMA_NAME = 3;
const int COLUMNS_TABLE_NAME = 4;
const int COLUMNS_COLUMN_NAME = 5;
const int COLUMNS_COLUMN_POSITION = 6;
const int COLUMNS_COLUMN_TYPE = 7;
const int COLUMNS_COLUMN_DATATYPE = 8;
const int COLUMNS_COLUMN_TYPENAME = 9;
const int COLUMNS_COLUMN_SUBTYPE = 10;
const int COLUMNS_COLUMN_PRECISION = 11;
const int COLUMNS_COLUMN_SCALE = 12;
const int COLUMNS_COLUMN_LENGTH = 13;
const int COLUMNS_COLUMN_NULLABLE = 14;
const int COLUMNS_COLTYPE = 15;
const int COLUMNS_XID = 16;
const int COLUMNS_TYPENAME = 17;


/////////////////////////////////////////////////////////////////////////////

const pCHAR PROC_PARAMS_SQL =
	"select \
		0 recno, \
		' ' catalog_name, \
		owner schema_name, \
		procname proc_name, \
		'0' param_name, \
		0 param_position, \
		1 param_type, \
		0 param_datatype, \
		0 param_subtype, \
		' ' param_typename, \
		0 param_length, \
		0 param_precision, \
		0 param_scale, \
		1 param_nullable, \
		numargs \
	from \
		informix.sysprocedures \
	where \
		procname like ? \
		and numargs > 0 \
	order by 4";

const int PROC_PARAMS_RECNO = 1;
const int PROC_PARAMS_CATALOG_NAME = 2;
const int PROC_PARAMS_SCHEMA_NAME = 3;
const int PROC_PARAMS_PROC_NAME = 4;
const int PROC_PARAMS_NAME = 5;
const int PROC_PARAMS_POSITION = 6;
const int PROC_PARAMS_TYPE = 7;
const int PROC_PARAMS_DATATYPE = 8;
const int PROC_PARAMS_SUBTYPE = 9;
const int PROC_PARAMS_TYPENAME = 10;
const int PROC_PARAMS_LENGTH = 11;
const int PROC_PARAMS_PRECISION = 12;
const int PROC_PARAMS_SCALE = 13;
const int PROC_PARAMS_NULLABLE = 14;
const int PROC_PARAMS_NUMARGS = 15;


/////////////////////////////////////////////////////////////////////////////

const pCHAR COLUMNS_SHORT_SQL =
	"select \
		st.tabid, \
		sc.colno, \
		sc.colname \
	from \
		informix.systables st, \
		informix.syscolumns sc \
	where \
		st.tabname like ? \
		and sc.tabid = st.tabid ";

const int COLUMNSSHORT_TABID = 1;
const int COLUMNSSHORT_COLNO = 2;
const int COLUMNSSHORT_COLNAME = 3;


/////////////////////////////////////////////////////////////////////////////

const pCHAR IS_DYNAMIC_INDICES_SQL =
	"select max(1) \
	from informix.systables t, informix.syscolumns c \
	where t.tabname = 'sysindexes' and c.tabid = t.tabid and c.colname = 'part16'";

const pCHAR INDICES_SQLS[] =
{
	// Informix-SE compatible
	"select \
		-1 recno, \
		' ' catalog_name, \
		si.owner schema_name, \
		st.tabname table_name, \
		si.idxname index_name, \
		scon.idxname pkey_name, \
		' ' column_name, \
		0 column_position, \
		0 index_type, \
		' ' sort_order, \
		' ' filter, \
		si.tabid, \
		si.idxtype, \
		si.part1, si.part2, si.part3, si.part4, si.part5, si.part6, si.part7, si.part8, 0 \
	from \
		informix.systables st, \
		informix.sysindexes si, \
		outer informix.sysconstraints scon \
	where \
		st.tabname like ? \
		and si.tabid = st.tabid \
		and scon.tabid = st.tabid \
		and scon.constrtype = 'P' \
	order by 3, 4, 5",

	// Informix Dynamic Server compatible
	"select \
		-1 recno, \
		' ' catalog_name, \
		si.owner schema_name, \
		st.tabname table_name, \
		si.idxname index_name, \
		scon.idxname pkey_name, \
		' ' column_name, \
		0 column_position, \
		0 index_type, \
		' ' sort_order, \
		' ' filter, \
		si.tabid, \
		si.idxtype, \
		si.part1, si.part2, si.part3, si.part4, si.part5, si.part6, si.part7, si.part8, \
		si.part9, si.part10, si.part11, si.part12, si.part13, si.part14, si.part15, si.part16, 0 \
	from \
		informix.systables st, \
		informix.sysindexes si, \
		outer informix.sysconstraints scon \
	where \
		st.tabname like ? \
		and si.tabid = st.tabid \
		and scon.tabid = st.tabid \
		and scon.constrtype = 'P' \
	order by 3, 4, 5"
};

const int INDICES_RECNO = 1;
const int INDICES_CATALOG_NAME = 2;
const int INDICES_SCHEMA_NAME = 3;
const int INDICES_TABLE_NAME = 4;
const int INDICES_INDEX_NAME = 5;
const int INDICES_PKEY_NAME = 6;
const int INDICES_COLUMN_NAME = 7;
const int INDICES_COLUMN_POSITION = 8;
const int INDICES_INDEX_TYPE = 9;
const int INDICES_SORT_ORDER = 10;
const int INDICES_FILTER = 11;
const int INDICES_TABID = 12;
const int INDICES_IDXTYPE = 13;
const int INDICES_PART_BEGIN = 14;


/////////////////////////////////////////////////////////////////////////////
// CInformixTablesCursor

CInformixTablesCursor::CInformixTablesCursor (CInformixCommand *command): CInformixTrimingCursor(command),
	_dbxTableType(
		reinterpret_cast<CInformixAbstractSimpleValue<eSQLTableType> *>(getColumn(TABLES_TABLE_TYPE).getValue())->operator eSQLTableType&()
	),
	_ifxTableType(
		**reinterpret_cast<CInformixAbstractStringValue *>(getColumn(TABLES_TABTYPE).getValue())
	)
{
}

SQL_METHOD_IMP CInformixTablesCursor::next ()
{
	SQLResult sqlResult;
	if ((sqlResult = CInformixCursor::next()) != SQL_SUCCESS)
		return sqlResult;

	switch (_ifxTableType)
	{
	case 'T': _dbxTableType = eSQLTable; break;
	case 'V': _dbxTableType = eSQLView; break;
	case 'P': case 'S': _dbxTableType = eSQLSynonym; break;
	default: _dbxTableType = eSQLTable;
	}

	return SQL_SUCCESS;
}


/////////////////////////////////////////////////////////////////////////////
// CInformixColumnsCursor

CInformixColumnsCursor::CInformixColumnsCursor (CInformixCommand *command): CInformixTrimingCursor(command),
	_dbxColumnType(
		reinterpret_cast<CInformixAbstractSimpleValue<INT32> *>(getColumn(COLUMNS_COLUMN_TYPE).getValue())->operator INT32&()
	),
	_dbxColumnDataType(
		reinterpret_cast<CInformixAbstractSimpleValue<INT32> *>(getColumn(COLUMNS_COLUMN_DATATYPE).getValue())->operator INT32&()
	),
	_dbxColumnSubType(
		reinterpret_cast<CInformixAbstractSimpleValue<INT32> *>(getColumn(COLUMNS_COLUMN_SUBTYPE).getValue())->operator INT32&()
	),
	_dbxColumnPrecision(
		reinterpret_cast<CInformixAbstractSimpleValue<INT32> *>(getColumn(COLUMNS_COLUMN_PRECISION).getValue())->operator INT32&()
	),
	_dbxColumnScale(
		reinterpret_cast<CInformixAbstractSimpleValue<INT32> *>(getColumn(COLUMNS_COLUMN_SCALE).getValue())->operator INT32&()
	),
	_dbxColumnLength(
		reinterpret_cast<CInformixAbstractSimpleValue<int2> *>(getColumn(COLUMNS_COLUMN_LENGTH).getValue())->operator int2&()
	),
	_dbxColumnNullable(
		reinterpret_cast<CInformixAbstractSimpleValue<INT32> *>(getColumn(COLUMNS_COLUMN_NULLABLE).getValue())->operator INT32&()
	),
	_ifxColumnType(
		reinterpret_cast<CInformixAbstractSimpleValue<int2> *>(getColumn(COLUMNS_COLTYPE).getValue())->operator int2&()
	),
	_ifxColumnXID(
		reinterpret_cast<CInformixAbstractSimpleValue<int4> *>(getColumn(COLUMNS_XID).getValue())->operator int4&()
	),
	_ifxColumnTypeName(
		*reinterpret_cast<CInformixAbstractStringValue *>(getColumn(COLUMNS_TYPENAME).getValue())
	)
{
	CColumnDescription& description = getDescription(COLUMNS_COLUMN_TYPENAME);
	description.setLength(INFORMIX_IDENTIFIER_LENGTH);
	description.setPrecision(INFORMIX_IDENTIFIER_LENGTH);
	CInformixAbstractStringValue *value = new CInformixStringValue<SQLCHAR>(INFORMIX_IDENTIFIER_LENGTH + 1);
	getColumn(COLUMNS_COLUMN_TYPENAME).setValue(value);
	_dbxColumnTypeName = *value;
}

SQL_METHOD_IMP CInformixColumnsCursor::next () SAFE_SQL_METHOD
{
	SQLResult sqlResult;
	if ((sqlResult = CInformixCursor::next()) != SQL_SUCCESS)
		return sqlResult;

	CColumnDescription description;

	CInformixColumnFactory::DescribeArgs args;
	args.mapping = getConnection()->getTypesMapping();
	args.type = _ifxColumnType;
	args.length = _dbxColumnLength;
	args.xid = _ifxColumnXID;
	args.description = &description;
	CInformixColumnFactory::describe(args);

	_dbxColumnType = description.isAutoIncrement() ? eSQLAutoIncr : 0;
	_dbxColumnDataType = description.getDataType().logType;
	if (_ifxColumnXID == 0)
		strcpy(_dbxColumnTypeName, rtypname(_ifxColumnType & SQLTYPE));
	else
		strcpy(_dbxColumnTypeName, _ifxColumnTypeName);
	_dbxColumnSubType = description.getDataType().subType;
	_dbxColumnPrecision = description.getPrecision();
	_dbxColumnScale = description.getScale();
	_dbxColumnLength = description.getLength();
	_dbxColumnNullable = description.isNullable();

	RETURN_SQL_RESULT;
}


/////////////////////////////////////////////////////////////////////////////
// CInformixProcParamsCursor

CInformixProcParamsCursor::CInformixProcParamsCursor (CInformixCommand *procParamsCommand):
	CInformixTrimingCursor(procParamsCommand),
	_dbxRecNo(
		reinterpret_cast<CInformixAbstractSimpleValue<INT32> *>(getColumn(PROC_PARAMS_RECNO).getValue())->operator INT32&()
	),
	_dbxPosition(
		reinterpret_cast<CInformixAbstractSimpleValue<INT32> *>(getColumn(PROC_PARAMS_POSITION).getValue())->operator INT32&()
	),
	_ifxNumArgs(
		reinterpret_cast<CInformixAbstractSimpleValue<int4> *>(getColumn(PROC_PARAMS_NUMARGS).getValue())->operator int4&()
	)
{
	_recordCounter = _paramCounter = _ifxNumArgs = 0;
	CColumnDescription& description = getDescription(PROC_PARAMS_NAME);
	description.setLength(INFORMIX_IDENTIFIER_LENGTH);
	description.setPrecision(INFORMIX_IDENTIFIER_LENGTH);
	CInformixAbstractStringValue *valueParamName = new CInformixStringValue<SQLCHAR>(INFORMIX_IDENTIFIER_LENGTH + 1);
	getColumn(PROC_PARAMS_NAME).setValue(valueParamName);
	_dbxParamName = *valueParamName;
}

SQL_METHOD_IMP CInformixProcParamsCursor::next () SAFE_SQL_METHOD
{
	if (_paramCounter == _ifxNumArgs) {
		SQLResult sqlResult;
		if ((sqlResult = CInformixTrimingCursor::next()) != SQL_SUCCESS)
			return sqlResult;
		_paramCounter = 0;
	}

	_dbxRecNo = ++_recordCounter;
	sprintf(_dbxParamName, "%d", ++_paramCounter);

	RETURN_SQL_RESULT;
}


/////////////////////////////////////////////////////////////////////////////
// CInformixIndicesCursor

CInformixIndicesCursor::CInformixIndicesCursor (CInformixCommand *indicesCommand, CInformixCommand *columnsCommand, UINT32 indexType):
	CInformixTrimingCursor(indicesCommand),
	_dbxRecNo(
		reinterpret_cast<CInformixAbstractSimpleValue<INT32> *>(getColumn(INDICES_RECNO).getValue())->operator INT32&()
	),
	_dbxIndexName(
		*reinterpret_cast<CInformixAbstractStringValue *>(getColumn(INDICES_INDEX_NAME).getValue())
	),
	_dbxPrimaryKeyName(
		*reinterpret_cast<CInformixAbstractStringValue *>(getColumn(INDICES_PKEY_NAME).getValue())
	),
	_dbxColumnPosition(
		reinterpret_cast<CInformixAbstractSimpleValue<INT32> *>(getColumn(INDICES_COLUMN_POSITION).getValue())->operator INT32&()
	),
	_dbxIndexType(
		reinterpret_cast<CInformixAbstractSimpleValue<INT32> *>(getColumn(INDICES_INDEX_TYPE).getValue())->operator INT32&()
	),
	_dbxSortOrder(
		**reinterpret_cast<CInformixAbstractStringValue *>(getColumn(INDICES_SORT_ORDER).getValue())
	),
	_ifxTabID(
		reinterpret_cast<CInformixAbstractSimpleValue<int4> *>(getColumn(INDICES_TABID).getValue())->operator int4&()
	),
	_ifxIndexType(
		**reinterpret_cast<CInformixAbstractStringValue *>(getColumn(INDICES_IDXTYPE).getValue())
	)
{
	_indexTypeFilter = indexType;
	_recordCounter = _currentIndexColumn = 0;
	readColumnsOfTables(columnsCommand);
	CColumnDescription& description = getDescription(INDICES_COLUMN_NAME);
	description.setLength(INFORMIX_IDENTIFIER_LENGTH);
	description.setPrecision(INFORMIX_IDENTIFIER_LENGTH);
	CInformixAbstractStringValue *valueColumnName = new CInformixStringValue<SQLCHAR>(INFORMIX_IDENTIFIER_LENGTH + 1);
	getColumn(INDICES_COLUMN_NAME).setValue(valueColumnName);
	_dbxColumnName = *valueColumnName;
}

SQL_METHOD_IMP CInformixIndicesCursor::next () SAFE_SQL_METHOD
{
	fetchNextRecord();

	_dbxRecNo = ++_recordCounter;
	_dbxIndexType = _indexType;
	if (_currentIndexColumn < 0) {
		_dbxSortOrder = 'D';
		_currentIndexColumn = -_currentIndexColumn;
	} else
		_dbxSortOrder = 'A';
	strcpy(_dbxColumnName, (*_currentTableColumns)[_currentIndexColumn]);
	_dbxColumnPosition = _currentIndexPart - INDICES_PART_BEGIN;

	RETURN_SQL_RESULT;
}

void CInformixIndicesCursor::fetchNextIndex ()
{
	while (true)
	{
		SQLResult sqlResult;
		if ((sqlResult = CInformixTrimingCursor::next()) != SQL_SUCCESS)
			throw sqlResult;
		if (_ifxIndexType == 'U') {
			_indexType = eSQLUnique;
			if (strcmp(_dbxIndexName, _dbxPrimaryKeyName) == 0)
				_indexType |= eSQLPrimaryKey;
		} else
			_indexType = eSQLNonUnique;
		if (_indexTypeFilter == 0 || (_indexType & _indexTypeFilter) != 0)
			break;
	}
	_currentTableColumns = &_tables[_ifxTabID];
	_currentIndexPart = INDICES_PART_BEGIN;
}

void CInformixIndicesCursor::fetchNextRecord ()
{
	while (true)
	{
		// array of index'es column parts is zero terminated,
		// if zero column part encountered then need to fetch next index
		if (_currentIndexColumn == 0)
			fetchNextIndex();
		_currentIndexColumn = *reinterpret_cast<CInformixAbstractSimpleValue<int2> *>(getColumn(_currentIndexPart++).getValue());
		if (_currentIndexColumn != 0)
			break;
	}
}

void CInformixIndicesCursor::readColumnsOfTables (CInformixCommand *columnsCommand)
{
	std::auto_ptr<CInformixTrimingCursor> columnsCursor(new CInformixTrimingCursor(columnsCommand));
	int4& tabid = *reinterpret_cast<CInformixAbstractSimpleValue<int4> *>(columnsCursor->getColumn(COLUMNSSHORT_TABID).getValue());
	int2& colno = *reinterpret_cast<CInformixAbstractSimpleValue<int2> *>(columnsCursor->getColumn(COLUMNSSHORT_COLNO).getValue());
	char *colname = *reinterpret_cast<CInformixAbstractStringValue *>(columnsCursor->getColumn(COLUMNSSHORT_COLNAME).getValue());

	SQLResult sqlResult;
	while ((sqlResult = columnsCursor->next()) == SQL_SUCCESS)
		_tables[tabid][colno] = colname;
	if (sqlResult != DBXERR_EOF)
		throw CInformixDriverError(columnsCursor->getErrorMessage());
}


/////////////////////////////////////////////////////////////////////////////
// CInformixMetaData

CInformixMetaData::CInformixMetaData (CInformixConnection *connection): _connection(0)
{
	(_connection = connection)->AddRef();
	setResourceDefaults();
	_errorRegister = getConnection();
	getConnection()->attachDatabaseChangeObserver(this);
}

CInformixMetaData::~CInformixMetaData ()
{
	if (_connection == 0)
		return;

	freeResources();

	_connection->detachDatabaseChangeObserver(this);
	_connection->Release();
}

CInformixConnection * CInformixMetaData::getConnection ()
{
	return _connection;
}

// implementation of SQLMetaData interface

SQL_METHOD_IMP CInformixMetaData::setOption (eSQLMetaDataOption eDOption, INT32 lValue)
{
	switch (eDOption)
	{
	case eMetaCatalogName:
		_database = reinterpret_cast<char *>(lValue); break;

	case eMetaSchemaName:
		_user = reinterpret_cast<char *>(lValue); break;

	case eMetaPackageName:
		return SQL_SUCCESS;

	default:
		return DBXERR_NOTSUPPORTED;
	}

	return SQL_SUCCESS;
}

SQL_METHOD_IMP CInformixMetaData::getOption (eSQLMetaDataOption eDOption, pINT32 plValue, INT16 iMaxLength, pINT16 piLength)
{
	switch (eDOption)
	{
	case eMetaSupportsTransaction:
		return _connection->getOption(eConnSupportsTransaction, plValue, iMaxLength, piLength);

	case eMetaSupportsTransactions:
		return _connection->getOption(eConnMultipleTransaction, plValue, iMaxLength, piLength);

	case eMetaPackageName:
		return SQL_SUCCESS;

	default:
		return DBXERR_NOTSUPPORTED;
	}
}

SQL_METHOD_IMP CInformixMetaData::getObjectList (eSQLObjectType eObjType, ppSQLCursor ppCur)
{
	return DBXERR_NOTSUPPORTED;
}

SQL_METHOD_IMP CInformixMetaData::getTables (pCHAR pszTableName, UINT32 uTableType, ppSQLCursor ppCur) SAFE_SQL_METHOD
{
	pszTableName = checkPattern(pszTableName);
	CInformixActivityPtr activity = getConnection()->activate(false, true);
	if ((uTableType & eSQLTable) != 0)
		getTablesCommand()->getParameters()[1].setValue(new CInformixStringValue<SQLCHAR>(2, "T")); else
		getTablesCommand()->getParameters()[1].setNull();
	if ((uTableType & eSQLView) != 0)
		getTablesCommand()->getParameters()[2].setValue(new CInformixStringValue<SQLCHAR>(2, "V")); else
		getTablesCommand()->getParameters()[2].setNull();
	if ((uTableType & eSQLSynonym) != 0) {
		getTablesCommand()->getParameters()[3].setValue(new CInformixStringValue<SQLCHAR>(2, "P"));
		getTablesCommand()->getParameters()[4].setValue(new CInformixStringValue<SQLCHAR>(2, "S"));
	} else {
		getTablesCommand()->getParameters()[3].setNull();
		getTablesCommand()->getParameters()[4].setNull();
	}
	if ((uTableType & eSQLSystemTable) != 0)
		getTablesCommand()->getParameters()[5].setValue(new CInformixSimpleValue<int, SQLINT>(1)); else
		getTablesCommand()->getParameters()[5].setNull();
	getTablesCommand()->getParameters()[6].setValue(new CInformixStringValue<SQLCHAR>(strlen(pszTableName) + 1, pszTableName));
	*ppCur = new CInformixTablesCursor(getTablesCommand());
	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixMetaData::getProcedures (pCHAR pszProcName, UINT32 uProcType, ppSQLCursor ppCur) SAFE_SQL_METHOD
{
	pszProcName = checkPattern(pszProcName);
	CInformixActivityPtr activity = getConnection()->activate(false, true);
	getProceduresCommand()->getParameters()[1].setValue(new CInformixStringValue<SQLCHAR>(strlen(pszProcName) + 1, pszProcName));
	*ppCur = new CInformixTrimingCursor(getProceduresCommand());
	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixMetaData::getColumns (pCHAR pszTableName, pCHAR pszColumnName, UINT32 uColType, ppSQLCursor ppCur) SAFE_SQL_METHOD
{
	pszTableName = checkPattern(pszTableName);
	pszColumnName = checkPattern(pszColumnName);
	CInformixActivityPtr activity = getConnection()->activate(false, true);
	getColumnsCommand()->getParameters()[1].setValue(new CInformixStringValue<SQLCHAR>(strlen(pszTableName) + 1, pszTableName));
	getColumnsCommand()->getParameters()[2].setValue(new CInformixStringValue<SQLCHAR>(strlen(pszColumnName) + 1, pszColumnName));
	*ppCur = new CInformixColumnsCursor(getColumnsCommand());;
	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixMetaData::getProcedureParams(pCHAR pszProcName, pCHAR pszParamName, ppSQLCursor ppCur) SAFE_SQL_METHOD
{
	pszProcName = checkPattern(pszProcName);
	pszParamName = checkPattern(pszParamName);
	CInformixActivityPtr activity = getConnection()->activate(false, true);
	getProcParamsCommand()->getParameters()[1].setValue(new CInformixStringValue<SQLCHAR>(strlen(pszProcName) + 1, pszProcName));
	*ppCur = new CInformixProcParamsCursor(getProcParamsCommand());
	RETURN_SQL_RESULT;
}

SQL_METHOD_IMP CInformixMetaData::getIndices (pCHAR pszTableName, UINT32 uIndexType, ppSQLCursor ppCur) SAFE_SQL_METHOD
{
	pszTableName = checkPattern(pszTableName);
	CInformixActivityPtr activity = getConnection()->activate(false, true);
	getIndicesCommand()->getParameters()[1].setValue(new CInformixStringValue<SQLCHAR>(strlen(pszTableName) + 1, pszTableName));
	getColumnsShortCommand()->getParameters()[1].setValue(new CInformixStringValue<SQLCHAR>(strlen(pszTableName) + 1, pszTableName));
	*ppCur = new CInformixIndicesCursor(getIndicesCommand(), getColumnsShortCommand(), uIndexType);
	RETURN_SQL_RESULT;
}

char * CInformixMetaData::checkPattern (char *pszPattern)
{
	if ((pszPattern == 0) || (*pszPattern == 0))
		return "%";
	return pszPattern;
}

void CInformixMetaData::setResourceDefaults ()
{
	_commandTables = 0;
	_commandProcedures = 0;
	_commandColumns = 0;
	_commandProcParams = 0;
	_commandColumnsShort = 0;
	_commandIndices = 0;
}

void CInformixMetaData::freeResources ()
{
	if (_commandTables != 0)
		_commandTables->Release();
	if (_commandProcedures != 0)
		_commandProcedures->Release();
	if (_commandColumns != 0)
		_commandColumns->Release();
	if (_commandProcParams != 0)
		_commandProcParams->Release();
	if (_commandColumnsShort != 0)
		_commandColumnsShort->Release();
	if (_commandIndices != 0)
		_commandIndices->Release();
}

CInformixCommand * CInformixMetaData::getTablesCommand ()
{
	if (_commandTables == 0)
	{
		_commandTables = new CInformixCommand(_connection);
		try {
			if (_commandTables->prepare(TABLES_SQL, 6) != SQL_SUCCESS)
				throw CInformixDriverError(_commandTables->getErrorMessage());
		} catch (...) {
			delete _commandTables;
			_commandTables = 0;
			throw;
		}
	}
	return _commandTables;
}

CInformixCommand * CInformixMetaData::getProceduresCommand ()
{
	if (_commandProcedures == 0)
	{
		_commandProcedures = new CInformixCommand(_connection);
		try {
			if (_commandProcedures->prepare(PROCEDURES_SQL, 1) != SQL_SUCCESS)
				throw CInformixDriverError(_commandProcedures->getErrorMessage());
		} catch (...) {
			delete _commandProcedures;
			_commandProcedures = 0;
			throw;
		}
	}
	return _commandProcedures;
}

CInformixCommand * CInformixMetaData::getColumnsCommand ()
{
	if (_commandColumns == 0)
	{
		_commandColumns = new CInformixCommand(_connection);
		try {
			if (_commandColumns->prepare(COLUMNS_SQLS[_connection->isUniversal() ? 1 : 0], 2) != SQL_SUCCESS)
				throw CInformixDriverError(_commandColumns->getErrorMessage());
		} catch (...) {
			delete _commandColumns;
			_commandColumns = 0;
			throw;
		}
	}
	return _commandColumns;
}

CInformixCommand * CInformixMetaData::getProcParamsCommand ()
{
	if (_commandProcParams == 0)
	{
		_commandProcParams = new CInformixCommand(_connection);
		try {
			if (_commandProcParams->prepare(PROC_PARAMS_SQL, 1) != SQL_SUCCESS)
				throw CInformixDriverError(_commandProcParams->getErrorMessage());
		} catch (...) {
			delete _commandProcParams;
			_commandProcParams = 0;
			throw;
		}
	}
	return _commandProcParams;
}

CInformixCommand * CInformixMetaData::getColumnsShortCommand ()
{
	if (_commandColumnsShort == 0)
	{
		_commandColumnsShort = new CInformixCommand(_connection);
		try {
			if (_commandColumnsShort->prepare(COLUMNS_SHORT_SQL, 1) != SQL_SUCCESS)
				throw CInformixDriverError(_commandColumnsShort->getErrorMessage());
		} catch (...) {
			delete _commandColumnsShort;
			_commandColumnsShort = 0;
			throw;
		}
	}
	return _commandColumnsShort;
}

// Workaround for the INOFRMIX-SE 7.25 IsOnLine bug
bool CInformixMetaData::isOnLineIndices ()
{
	CInformixActivityPtr activity = _connection->activate(false, false);
	const char *sqlcmdtxt[] = { IS_DYNAMIC_INDICES_SQL, 0 };
	ifx_sqlvar_t sqlvar = { 0 };
	sqlvar.sqltype = CINTTYPE;
	int4 result;
	sqlvar.sqllen = sizeof(result);
	sqlvar.sqldata = reinterpret_cast<char *>(&result);
	ifx_cursor_t cursor = { 0 };
	checkInformixResult(sqli_slct(ESQLINTVERSION, &cursor, StringArrayCast(sqlcmdtxt), 0, 0, 1, &sqlvar, 0, 0, 0, 0));
	return result == 1;
}

CInformixCommand * CInformixMetaData::getIndicesCommand ()
{
	if (_commandIndices == 0)
	{
		_commandIndices = new CInformixCommand(_connection);
		try {
			if (_commandIndices->prepare(INDICES_SQLS[_connection->getIsOnLine() && isOnLineIndices() ? 1 : 0], 1) != SQL_SUCCESS)
				throw CInformixDriverError(_commandIndices->getErrorMessage());
		} catch (...) {
			delete _commandIndices;
			_commandIndices = 0;
			throw;
		}
	}
	return _commandIndices;
}

void CInformixMetaData::databaseChanged ()
{
	freeResources();
	setResourceDefaults();
}
